﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Needle.Utils.Threading
{

    //TODO write tests
    /// <remarks>
    /// adapted from http://blogs.msdn.com/ploeh/archive/2007/02/09/AGenericIAsyncResultImplementation.aspx
    /// </remarks>
    public class AsyncResult<T> : IAsyncResult, IDisposable
    {

        private bool _completed;
        private bool _completedSynchronously;

        private T _result;
        private Exception _e;
        private readonly AsyncCallback _callback;
        private readonly object _syncRoot;
        /// <summary>
        ///  
        /// </summary>
        public AsyncResult(AsyncCallback callback, object state)
            : this(callback, state, false)
        { }
        /// <summary>
        ///  
        /// </summary>
        public AsyncResult(AsyncCallback callback, object state,  bool completed)
        {
            _callback = callback;
            AsyncState = state;
            _completed = completed;
            _completedSynchronously = completed;
            _asyncWaitHandle = new ManualResetEvent(false);
            _syncRoot = new object();
        }

        #region IAsyncResult Members
        /// <summary>
        ///  
        /// </summary>
        public object AsyncState { get; private set; }

        private ManualResetEvent _asyncWaitHandle;
        /// <summary>
        ///  
        /// </summary>
        public WaitHandle AsyncWaitHandle
        {
            get { return _asyncWaitHandle; }
        }
        /// <summary>
        ///  
        /// </summary>
        public bool CompletedSynchronously
        {
            get
            {
                lock (this._syncRoot)
                {
                    return this._completedSynchronously;
                }
            }
        }
        /// <summary>
        ///  
        /// </summary>
        public bool IsCompleted
        {
            get
            {
                lock (this._syncRoot)
                {
                    return this._completed;
                }
            }
        }

        #endregion

        #region IDisposable Members
        /// <summary>
        ///  
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }
        /// <summary>
        ///  
        /// </summary>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                lock (this._syncRoot)
                {
                    if (this.AsyncWaitHandle != null)
                    {
                        ((IDisposable)this.AsyncWaitHandle).Dispose();
                    }
                }
            }
        }

        #endregion

        /// <summary>
        ///  
        /// </summary>
        public Exception Exception
        {
            get
            {
                lock (this._syncRoot)
                {
                    return this._e;
                }
            }
        }

        /// <summary>
        ///  
        /// </summary>
        public T Result
        {
            get
            {
                lock (this._syncRoot)
                {
                    return this._result;
                }
            }
        }

        /// <summary>
        ///  
        /// </summary>
        public void Complete(T result, bool completedSynchronously)
        {
            lock (this._syncRoot)
            {
                _completed = true;
                _completedSynchronously = completedSynchronously;
                _result = result;
            }
            this.SignalCompletion();
        }

        /// <summary>
        ///  
        /// </summary>
        public void HandleException(Exception e, bool completedSynchronously)
        {
            lock (this._syncRoot)
            {
                _completed = true;
                _completedSynchronously = completedSynchronously;
                _e = e;
            }
            this.SignalCompletion();
        }

        private void SignalCompletion()
        {
            _asyncWaitHandle.Set();
            ThreadPool.QueueUserWorkItem(InvokeCallback);
        }

        private void InvokeCallback(object unused)
        {
            if (this._callback != null)
            {
                this._callback(this);
            }
        }
    }
}
